﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace SPCLib
{
    using System;
    
    public class GroupItem
    {
        public double? BeginVal { get; set; }
        public double? EndVal { get; set; }
        public double? CenterVal { get; set; }
        public int qty { get; set; }
        public int total { get; set; }
        public string GroupTitle { get; set; }
        public double Percent { get { return (qty * 1.0 / total); } }
        public int systemFail { get; set; }
    }
    public class StatisticsVals
    {
        
    }
    public class SPCFunctions
    {
        private double CL;
        private const string HistogramGrouInfo = "50-100个样本分成6-10组，100-250个样本分成7-12组，250个以上样本分成10-25组";
        private int HistogramGroupNumber = 9;
        private const string HistogramGroupTips = "对于绝大多数用户来讲,50个样本具备足够的可靠性，测量成本低时，为了达到准确，尽量将样本数量满足100个以上";
        private double LCL;
        private double LSL;
        private double[] Samples;
        private List<double> LowerValues = new List<double>();
        private List<double> UpperValues = new List<double>();
        private double UCL;
        private double USL;
        public int UpperCount { get { return UpperValues.Count; } }
        public int LowerCount { get { return LowerValues.Count; } }
        public int SystemFailCount { get { return UpperCount + LowerCount; } }
        public double[] MarkVals { get; set; }
        public bool UseMarkVals { get; set; }
        public int total { get; set; }
        public int SystemFail { get { return( UpperCount + LowerCount); } }

        public SPCFunctions()
        {
        }

        //带入双精度数组和规格上下限
        /// <summary>
        /// 带入双精度数组和规格上下限
        /// </summary>
        /// <param name="doublearray">带入双精度数组</param>
        /// <param name="usl">上限值</param>
        /// <param name="lsl">下限值</param>
        public SPCFunctions(double[] doublearray, double usl, double lsl)
        {
            total = doublearray.Length;
            this.USL = usl;
            this.LSL = lsl;
            this.Samples = RemoveAbnormalValue(doublearray);
            //     this.Samples = doublearray;
            XbarChart();
        }

        private double[] RemoveAbnormalValue(double[] array)
        {
            List<double> sample = new List<double>();
            foreach (double d in array)
            {
                if (d < LSL * 0.94)
                {
                    LowerValues.Add(d);
                }
                else if (d > USL * 1.06)
                {
                    UpperValues.Add(d);
                }
                else
                {
                    sample.Add(d);
                }
            }
            return sample.ToArray();
        }
        /// <summary>
        /// 带入整数数组和规格上下限
        /// </summary>
        /// <param name="doublearray">带入整数数组</param>
        /// <param name="usl">上限值</param>
        /// <param name="lsl">下限值</param>
        public SPCFunctions(int[] intarray, int usl, int lsl)
        {
            for (int i = 0; i < intarray.Length; i++)
            {
                this.Samples[i] = intarray[i];
            }
            this.USL = usl;
            this.LSL = lsl;
        }

        /// <summary>
        /// 带入浮点数组和规格上下限
        /// </summary>
        /// <param name="doublearray">带入浮点数组</param>
        /// <param name="usl">上限值</param>
        /// <param name="lsl">下限值</param>
        public SPCFunctions(float[] floatarray, float usl, float lsl)
        {
            for (int i = 0; i < floatarray.Length; i++)
            {
                this.Samples[i] = floatarray[i];
            }
            this.USL = usl;
            this.LSL = lsl;
        }

        private static double erf(double z)
        {
            double t = 1.0 / (1.0 + (0.5 * Math.Abs(z)));
            double ans = 1.0 - (t * Math.Exp(((-z * z) - 1.26551223) + (t * (1.00002368 + (t * (0.37409196 + (t * (0.09678418 + (t * (-0.18628806 + (t * (0.27886807 + (t * (-1.13520398 + (t * (1.48851587 + (t * (-0.82215223 + (t * 0.17087277)))))))))))))))))));
            if (z >= 0.0)
            {
                return ans;
            }
            return -ans;
        }
        private static double Phi(double z)
        {
            return (0.5 * (1.0 + erf(z / Math.Sqrt(2.0))));
        }

        private static double Phi(double z, double mu, double sigma)
        {
            return Phi((z - mu) / sigma);
        }

        public void setCL(double CL)
        {
            this.CL = CL;
        }

        public void setHistogramGroupNumber(int HistogramGroupNumber)
        {
            this.HistogramGroupNumber = HistogramGroupNumber;
        }

        public void setLCL(double LCL)
        {
            this.LCL = LCL;
        }

        public void setLSL(double LSL)
        {
            this.LSL = LSL;
        }

        public void setSamples(double[] samples)
        {
            this.Samples = samples;
        }

        public void setUCL(double UCL)
        {
            this.UCL = UCL;
        }

        public void setUSL(double USL)
        {
            this.USL = USL;
        }

        public double SPCCp()
        {
            return ((this.USL - this.LSL) / (6.0 * this.SPCStdDev()));
        }

        public double SPCCpk()
        {
            return Math.Min(this.SPCCpu(), this.SPCCpl());
        }

        public double SPCCpl()
        {
            return ((this.SPCMean() - this.LSL) / (3.0 * this.SPCStdDev()));
        }

        public double SPCCpm()
        {
            return ((this.USL - this.LSL) / (6.0 * Math.Sqrt(Math.Pow(this.SPCStdDev(), 2.0) + Math.Pow(this.SPCMean() - (this.USL - this.LSL), 2.0))));
        }
        /// <summary>
        /// CPU
        /// </summary>
        /// <returns></returns>
        public double SPCCpu()
        {
            return ((this.USL - this.SPCMean()) / (3.0 * this.SPCStdDev()));
        }

        public double SPCCr()
        {
            return ((6.0 * this.SPCStdDev()) / (this.USL - this.LSL));
        }

        public double SPCFpCap()
        {
            return (this.SPCFpuCap() + this.SPCFplCap());
        }

        public double SPCFplCap()
        {
            return (1.0 - Phi(this.SPCCpl() * 3.0));
        }

        public double SPCFplPerf()
        {
            return (1.0 - Phi(3.0 * this.SPCPpl()));
        }

        public double SPCFpPerf()
        {
            return (this.SPCFpuPerf() + this.SPCFplPerf());
        }

        public double SPCFpuCap()
        {
            return (1.0 - Phi(this.SPCCpu() * 3.0));
        }

        public double SPCFpuPerf()
        {
            return (1.0 - Phi(3.0 * this.SPCPpu()));
        }
        /// <summary>
        /// CPK
        /// </summary>
        /// <returns></returns>
        public double SPCk()
        {
            return ((Math.Abs((double)(((this.USL + this.LSL) / 2.0) - this.SPCMean())) / (this.USL - this.LSL)) * 2.0);
        }
        /// <summary>
        /// 峰值系数
        /// </summary>
        /// <returns></returns>
        public double SPCKurtosis()
        {
            double sumup4 = 0.0;
            for (int i = 0; i < this.Samples.Length; i++)
            {
                sumup4 += Math.Pow(this.Samples[i] - this.SPCMean(), 4.0);
            }
            return ((((this.Samples.Length * (this.Samples.Length + 1)) * sumup4) / ((((this.Samples.Length - 1) * (this.Samples.Length - 2)) * (this.Samples.Length - 3)) * Math.Pow(this.SPCStdDev(), 4.0))) - ((3.0 * Math.Pow((double)(this.Samples.Length - 1), 2.0)) / ((double)((this.Samples.Length - 2) * (this.Samples.Length - 3)))));
        }
        /// <summary>
        /// 最大值
        /// </summary>
        /// <returns></returns>
        public double SPCMax()
        {
          return  Samples.Max();
        }
        /// <summary>
        /// 平均值
        /// </summary>
        /// <returns></returns>
        public double SPCMean()
        {
            double sumup = 0.0;
            for (int i = 0; i < this.Samples.Length; i++)
            {
                sumup += this.Samples[i];
            }
            return (sumup / ((double)this.Samples.Length));
        }
        /// <summary>
        /// 最小值
        /// </summary>
        /// <returns></returns>
        public double SPCMin()
        {
            return Samples.Min();
        }

        public double Min(double[] arr, double val)
        {
            if (arr.Length == 0)
                return val;
            double min = arr[0];
            for (int i = 0; i < arr.Length; i++)
            {
                min = Math.Min(min, arr[i]);
            }
            return min;
        }
        public double Max(double[] arr, double val)
        {
            if (arr.Length == 0)
                return val;
            double max = arr[0];
            for (int i = 0; i < arr.Length; i++)
            {
                max = Math.Max(max, arr[i]);
            }
            return max;
        }
        /// <summary>
        /// 输出cp
        /// </summary>
        /// <returns></returns>
        public double SPCPp()
        {
            return (((this.USL - this.LSL) / 6.0) / this.SPCStdDev());
        }

        public double SPCPpk()
        {
            return Math.Min(this.SPCPpu(), this.SPCPpl());
        }
        /// <summary>
        /// 输出PPL
        /// </summary>
        /// <returns></returns>
        public double SPCPpl()
        {
            return ((this.SPCMean() - this.LSL) / (3.0 * this.SPCStdDev()));
        }

        public double SPCPpm()
        {
            return ((this.USL - this.LSL) / (6.0 * Math.Sqrt(Math.Pow(this.SPCStdDev(), 2.0) + Math.Pow(this.SPCMean() - (this.USL - this.LSL), 2.0))));
        }
        /// <summary>
        /// 输出PPU
        /// </summary>
        /// <returns></returns>
        public double SPCPpu()
        {
            return ((this.USL - this.SPCMean()) / (3.0 * this.SPCStdDev()));
        }

        public double SPCPr()
        {
            return (1.0 / this.SPCPp());
        }

        public double SPCRange()
        {
            return (this.SPCMax() - this.SPCMin());
        }

        public double SPCSkewness()
        {
            double sumup3 = 0.0;
            for (int i = 0; i < this.Samples.Length; i++)
            {
                sumup3 += Math.Pow(this.Samples[i] - this.SPCMean(), 3.0);
            }
            return ((this.Samples.Length * sumup3) / (((this.Samples.Length - 1) * (this.Samples.Length - 2)) * Math.Pow(this.SPCStdDev(), 3.0)));
        }

        public double SPCStdDev()
        {
            double mean = this.SPCMean();
            double stddev = 0.0;
            for (int i = 0; i < this.Samples.Length; i++)
            {
                stddev += (this.Samples[i] - mean) * (this.Samples[i] - mean);
            }
            return Math.Sqrt(stddev / ((double)(this.Samples.Length - 1)));
        }

        public double SPCZlCap()
        {
            return (this.SPCCpl() * 3.0);
        }

        public double SPCZlPerf()
        {
            return (3.0 * this.SPCPpl());
        }

        public double SPCZuCap()
        {
            return (this.SPCCpu() * 3.0);
        }

        public double SPCZuPerf()
        {
            return (3.0 * this.SPCPpu());
        }

        public void XbarChart()
        {
            this.CL = this.SPCMean();
            double range = this.SPCRange();
            this.UCL = this.CL + (3 * SPCStdDev());
            this.LCL = this.CL - (3 * SPCStdDev());
        }


        //定义CL
        public double getCL()
        {
            return this.CL;
        }

        //定义正方图测量组别
        public int getHistogramGroupNumber()
        {
            return this.HistogramGroupNumber;
        }

        //定义LCL
        public double getLCL()
        {
            return this.LCL;
        }

        //定义LSL
        public double getLSL()
        {
            return this.LSL;
        }

        //获得样品测量数据
        public double[] getSamples()
        {
            return this.Samples;
        }

        public double getSL()
        {
            return ((this.USL + this.LSL) / 2.0);
        }

        public double getUCL()
        {
            return this.UCL;
        }

        public double getUSL()
        {
            return this.USL;
        }
        public static double NORMDIST(double x, double mean, double std, bool cumulative)
        {
            if (cumulative)
            {
                return Phi(x, mean, std);
            }
            double tmp = 1.0 / (Math.Sqrt(6.2831853071795862) * std);
            return (tmp * Math.Exp(-0.5 * Math.Pow((x - mean) / std, 2.0)));
        }

        public List<GroupItem> getGroupItems()
        {
            List<GroupItem> gis = new List<GroupItem>();
            if (UseMarkVals)
            {
                for (int i = 0; i <= MarkVals.Length; i++)
                {
                    if (i == 0)
                    {
                        GroupItem gi = new GroupItem();
                        gi.BeginVal = null;
                        gi.CenterVal = null;
                        gi.EndVal = MarkVals[i];
                        gi.GroupTitle = string.Format("<{0}", gi.EndVal);
                        gi.total = total;
                        gi.systemFail = SystemFail;
                        for (int j = 0; j < this.Samples.Length; j++)
                        {
                            if ((this.Samples[j] <=gi.EndVal))
                            {
                                gi.qty++;
                            }
                        }
                        gis.Add(gi);                       
                   }
                    else if (i == MarkVals.Length )
                    {
                        GroupItem gi = new GroupItem();
                        gi.BeginVal = MarkVals[i-1];
                        gi.CenterVal = null;
                        gi.EndVal =null;
                        gi.GroupTitle = string.Format(">{0}", gi.BeginVal);
                        gi.total = total;
                        for (int j = 0; j < this.Samples.Length; j++)
                        {
                            if ((this.Samples[j] > gi.BeginVal))
                            {
                                gi.qty++;
                            }
                        }
                        gis.Add(gi);
                    }
                    else
                    {
                        GroupItem gi = new GroupItem();
                        gi.BeginVal =Math.Round( MarkVals[i-1],3);
                        gi.CenterVal = Math.Round((MarkVals[i - 1] + MarkVals[i])/2,3);
                        gi.EndVal =Math.Round( MarkVals[i],3);
                        gi.GroupTitle = string.Format("{0}", gi.EndVal);
                        gi.total = total;
                        for (int j = 0; j < this.Samples.Length; j++)
                        {
                            if ((this.Samples[j] <= gi.EndVal) && (this.Samples[j] > gi.BeginVal))
                            {
                                gi.qty++;
                            }
                        }
                        gis.Add(gi);
                    }
                }
            }
            else
            {
                double min = LSL;
                double max = USL;
                double step = (USL - LSL) / HistogramGroupNumber;
                GroupItem giB = new GroupItem();
                giB.BeginVal =null;
                giB.EndVal =Math.Round( min,3);
                giB.CenterVal =null;
                giB.GroupTitle =string.Format("<{0}",giB.EndVal);
                giB.total = total;
                giB.systemFail = SystemFail;
               
                for (int j = 0; j < this.Samples.Length; j++)
                {
                    if ((this.Samples[j] <=giB.EndVal))
                    {
                        giB.qty++;
                    }
                }
                gis.Add(giB);
                for (int i = 0; i < HistogramGroupNumber; i++)
                {
                    GroupItem gi = new GroupItem();
                    gi.BeginVal =Math.Round( min + step * i,3);
                    gi.EndVal=Math.Round( min+ step * (i+1),3);
                    gi.CenterVal =Math.Round(min+step*i+step*0.5,3);
                    gi.GroupTitle = gi.EndVal.ToString();
                    gi.total = total;
                    for (int j = 0; j < this.Samples.Length; j++)
                    {
                        if ((this.Samples[j] <= gi.EndVal)&&(this.Samples[j] >gi.BeginVal))
                        {
                            gi.qty++;
                        }
                    }
                    gis.Add(gi);
                }
                GroupItem giE = new GroupItem();
                giE.BeginVal =Math.Round(max, 3);
                giE.EndVal = null;
                giE.CenterVal = null;
                giE.GroupTitle = string.Format(">{0}", giE.BeginVal);
                giE.total = total;                
                for (int j = 0; j < this.Samples.Length; j++)
                {
                    if ((this.Samples[j] >giE.BeginVal))
                    {
                        giE.qty++;
                    }
                }
                gis.Add(giE);
                return gis;
            }
            return gis;
        }
        public double[][] HistogramStepUpperLowerMidCountNormCumu()
        {
            double[][] HistogramUpperLowerMidCountNormCumu = new double[][] { new double[this.HistogramGroupNumber + 4], new double[this.HistogramGroupNumber + 4], new double[this.HistogramGroupNumber + 4], new double[this.HistogramGroupNumber + 4], new double[this.HistogramGroupNumber + 4], new double[this.HistogramGroupNumber + 4] };
            double mean = this.SPCMean();
            double std = this.SPCStdDev();
            double step = 6 * std / ((double)this.HistogramGroupNumber);
            double min = mean - 3 * std;
            HistogramUpperLowerMidCountNormCumu[0][0] = Math.Round(Min(LowerValues.ToArray(), (min - (step / 2.0)) - step), 3);
            HistogramUpperLowerMidCountNormCumu[2][0] = Math.Round((min - (step / 2.0)) + ((1 - 1) * step), 3);
            HistogramUpperLowerMidCountNormCumu[1][0] = Math.Round(min - std - (step / 2.0), 3);
            HistogramUpperLowerMidCountNormCumu[3][0] = LowerCount;
            HistogramUpperLowerMidCountNormCumu[4][0] = Math.Round(NORMDIST(min - std, mean, std, false), 3);
            HistogramUpperLowerMidCountNormCumu[5][0] = Math.Round(NORMDIST(min - std, mean, std, true), 3);
            for (int j = 0; j < this.Samples.Length; j++)
            {
                if ((Samples[j] < min))
                {
                    HistogramUpperLowerMidCountNormCumu[3][0]++;
                }
            }
            for (int i = 1; i <= (this.HistogramGroupNumber + 2); i++)
            {
                HistogramUpperLowerMidCountNormCumu[0][i] = Math.Round((min - (step / 2.0)) + ((i - 1) * step), 3);
                HistogramUpperLowerMidCountNormCumu[2][i] = Math.Round((min + (step / 2.0)) + ((i - 1) * step), 3);
                HistogramUpperLowerMidCountNormCumu[1][i] = Math.Round(min + ((i - 2) * step), 3);
                HistogramUpperLowerMidCountNormCumu[3][i] = 0.0;
                HistogramUpperLowerMidCountNormCumu[4][i] = Math.Round(NORMDIST(min + ((i - 1) * step), mean, std, false), 3);
                HistogramUpperLowerMidCountNormCumu[5][i] = Math.Round(NORMDIST(min + ((i - 1) * step), mean, std, true), 3);
                for (int j = 0; j < this.Samples.Length; j++)
                {
                    if ((this.Samples[j] >= HistogramUpperLowerMidCountNormCumu[0][i]) && (this.Samples[j] < HistogramUpperLowerMidCountNormCumu[2][i]))
                    {
                        HistogramUpperLowerMidCountNormCumu[3][i - 1]++;
                    }
                }
            }
            double max = (min + (step / 2.0)) + ((HistogramGroupNumber + 2 - 1) * step);

            HistogramUpperLowerMidCountNormCumu[0][HistogramGroupNumber + 3] = Math.Round(max, 3);
            HistogramUpperLowerMidCountNormCumu[2][HistogramGroupNumber + 3] = Math.Round(Math.Max(Max(UpperValues.ToArray(), max + (step / 2.0)), max + std), 3);
            HistogramUpperLowerMidCountNormCumu[1][HistogramGroupNumber + 3] = Math.Round(max + std, 3);
            HistogramUpperLowerMidCountNormCumu[3][HistogramGroupNumber + 3] = UpperCount;
            HistogramUpperLowerMidCountNormCumu[4][HistogramGroupNumber + 3] = Math.Round(NORMDIST(max + std, mean, std, false), 3);
            HistogramUpperLowerMidCountNormCumu[5][HistogramGroupNumber + 3] = Math.Round(NORMDIST(max + std, mean, std, true), 3);
            for (int j = 0; j < this.Samples.Length; j++)
            {
                if ((Samples[j] < min))
                {
                    HistogramUpperLowerMidCountNormCumu[3][HistogramGroupNumber + 3]++;
                }
            }
            this.XbarChart();
            return HistogramUpperLowerMidCountNormCumu;
        }
        public double[][] NormalStepUpperLowerMidCountNormCumu()
        {
            this.HistogramGroupNumber = 100;
            double[][] HistogramUpperLowerMidCountNormCumu = new double[][] { new double[this.HistogramGroupNumber + 2], new double[this.HistogramGroupNumber + 2], new double[this.HistogramGroupNumber + 2], new double[this.HistogramGroupNumber + 2], new double[this.HistogramGroupNumber + 2], new double[this.HistogramGroupNumber + 2] };
            double step = this.SPCStdDev() * 6 / ((double)this.HistogramGroupNumber);
            double min = this.SPCMean() - 3 * SPCStdDev();
            double mean = this.SPCMean();
            double std = this.SPCStdDev();
            for (int i = 0; i <= (this.HistogramGroupNumber + 1); i++)
            {
                HistogramUpperLowerMidCountNormCumu[0][i] = (min - (step / 2.0)) + (i * step);
                HistogramUpperLowerMidCountNormCumu[2][i] = (min + (step / 2.0)) + (i * step);
                HistogramUpperLowerMidCountNormCumu[1][i] = min + (i * step);
                HistogramUpperLowerMidCountNormCumu[3][i] = 0.0;
                HistogramUpperLowerMidCountNormCumu[4][i] = NORMDIST(min + (i * step), mean, std, false);
                HistogramUpperLowerMidCountNormCumu[5][i] = NORMDIST(min + (i * step), mean, std, true);
                for (int j = 0; j < this.Samples.Length; j++)
                {
                    if ((this.Samples[j] >= HistogramUpperLowerMidCountNormCumu[0][i]) && (this.Samples[j] < HistogramUpperLowerMidCountNormCumu[2][i]))
                    {
                        HistogramUpperLowerMidCountNormCumu[3][i]++;
                    }
                }
            }
            this.XbarChart();
            return HistogramUpperLowerMidCountNormCumu;
        }

    }
    
}

